package cn.lingyangwl.agile.infra.weixin.cp.process;

import cn.lingyangwl.agile.dynamic.config.core.ConfigFactory;
import cn.lingyangwl.agile.infra.weixin.cp.handler.*;
import cn.lingyangwl.agile.infra.weixin.cp.model.WxCpContext;
import cn.lingyangwl.agile.model.enums.ConfigDefineEnum;
import cn.lingyangwl.agile.model.module.config.ConfigInfo;
import cn.lingyangwl.agile.model.module.config.param.BaseConfigParam;
import cn.lingyangwl.agile.model.module.config.param.WxCpParam;
import cn.lingyangwl.agile.model.module.tenant.TenantContext;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxRuntimeException;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
import me.chanjar.weixin.cp.config.impl.WxCpRedissonConfigImpl;
import me.chanjar.weixin.cp.constant.WxCpConsts;
import me.chanjar.weixin.cp.message.WxCpMessageRouter;
import me.chanjar.weixin.mp.constant.WxMpEventConstants;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author shenguangyang
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WxCpProcess {
    private final static Map<String, WxCpContext> serviceCache = new ConcurrentHashMap<>();
    private final RedissonClient redissonClient;
    private final ConfigFactory configFactory;


    private final WxCpLocationHandler locationHandler;
    private final WxCpMenuHandler menuHandler;
    private final WxCpLogHandler logHandler;
    private final WxCpNullHandler nullHandler;
    private final WxCpSubscribeHandler subscribeHandler;
    private final WxCpUnsubscribeHandler unsubscribeHandler;
    private final WxCpEnterAgentHandler enterAgentHandler;
    private final WxCpScanHandler scanHandler;
    private final WxCpDefaultHandler defaultHandler;
    private final WxCpKfSessionHandler kfSessionHandler;


    private String getCacheKey() {
        String tenantId = TenantContext.get().getTenantId();
        return Optional.ofNullable(tenantId).orElseThrow(() -> new BizException("当前只支持租户环境下调用"));
    }

    /**
     * 获取公众号服务
     */
    public WxCpContext getContext() {
        String cacheKey = getCacheKey();
        WxCpContext context = serviceCache.get(cacheKey);
        if (Objects.isNull(context)) {
            synchronized (cacheKey.intern()) {
                context = serviceCache.get(cacheKey);
                if (Objects.nonNull(context)) {
                    return context;
                }
                context = createContext();
                serviceCache.put(cacheKey, context);
            }
        }
        return Optional.ofNullable(context).orElseThrow(() -> new WxRuntimeException("未配置此service"));
    }

    /**
     * 创建服务
     */
    public WxCpContext createContext() {
        ConfigInfo configInfo = configFactory.getConfig(ConfigDefineEnum.WX_CP);
        if (Objects.isNull(configInfo)) {
            throw new BizException("尚未配置企业微信, 请到控制台中配置企业微信");
        }

        WxCpParam cpParam = BaseConfigParam.jsonToObject(configInfo.getConfigKey(), configInfo.getConfigValue());

        WxCpRedissonConfigImpl config = new WxCpRedissonConfigImpl(redissonClient, WxCpContext.KEY_PRE);
        config.setCorpId(cpParam.getCorpId());
        config.setAgentId(Integer.parseInt(cpParam.getAgentId()));
        config.setCorpSecret(cpParam.getSecret());
        config.setToken(cpParam.getToken());
        config.setAesKey(cpParam.getAesKey());

        WxCpService service = new WxCpServiceImpl();
        service.setWxCpConfigStorage(config);

        WxCpContext cpContext = new WxCpContext();
        cpContext.setRouter(buildMessageRouter(service));
        cpContext.setService(service);
        cpContext.setParam(cpParam);
        return cpContext;
    }

    private WxCpMessageRouter buildMessageRouter(WxCpService service) {
        WxCpMessageRouter router = new WxCpMessageRouter(service);
        // 记录所有事件的日志（异步执行）
        router.rule().handler(logHandler).next();

        // 接收客服会话管理事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION)
                .handler(kfSessionHandler).end();
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION)
                .handler(kfSessionHandler).end();
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION)
                .handler(kfSessionHandler)
                .end();

        // 关注事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.SUBSCRIBE).handler(subscribeHandler)
                .end();

        // 取消关注事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.UNSUBSCRIBE)
                .handler(unsubscribeHandler).end();

        // 自定义菜单事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.MenuButtonType.CLICK).handler(menuHandler).end();

        // 点击菜单链接事件（这里使用了一个空的处理器，可以根据自己需要进行扩展）
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.MenuButtonType.VIEW).handler(nullHandler).end();

        // 扫码事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.SCAN).handler(scanHandler).end();

        // 上报地理位置事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.LOCATION).handler(locationHandler)
                .end();

        // 接收地理位置消息
        router.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION)
                .handler(locationHandler).end();

        // 进入应用事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxCpConsts.EventType.ENTER_AGENT).handler(enterAgentHandler).end();

        // 默认
        router.rule().async(false).handler(defaultHandler).end();
        return router;
    }

    /**
     * 删除缓存
     */
    public void deleteCache() {
        String cacheKey = getCacheKey();
//        WxCpContext cpContext = serviceCache.get(cacheKey);
        serviceCache.remove(cacheKey);
    }
}
